const express = require('express');
const { decrypt, getSignature } = require('@wecom/crypto');
const xml2js = require('xml2js');
const axios = require('axios');
const bodyParser = require('body-parser');
const NodeCache = require('node-cache');

const app = express();
const cache = new NodeCache();

require('dotenv').config()

// 环境变量（可替换为实际值或从 .env 加载）

const requiredEnvVars = {
  WXWORK_AGENTID: '企业微信应用AgentId',
  WXWORK_CORPID: '企业微信CorpID',
  WXWORK_CORPSECRET: '企业微信应用Secret',
  WXWORK_TOKEN: '企业微信回调Token',
  WXWORK_AESKEY: '企业微信回调EncodingAESKey',
  DIFY_APPTOKEN: 'Dify应用Token',
  DIFY_BASE_URL: 'Dify URL地址'
};

const missingVars = Object.entries(requiredEnvVars)
  .filter(([key]) => !process.env[key])
  .map(([key, name]) => name);

if (missingVars.length > 0) {
  throw new Error(`缺少必要的环境变量: ${missingVars.join(', ')}`);
}

const {
  WXWORK_AGENTID,
  WXWORK_CORPID,
  WXWORK_CORPSECRET,
  WXWORK_TOKEN,
  WXWORK_AESKEY,
  DIFY_APPTOKEN,
  DIFY_BASE_URL
} = process.env;

app.use(bodyParser.raw({
  type: 'text/xml',
  limit: '1mb'
}));

app.use((err, req, res, next) => {
  console.error('Body parsing error:', err);
  res.status(400).send('Invalid request body');
});

// 生成缓存 Key
function genConversationKey(userName) {
  return `${WXWORK_AGENTID}:${userName}`;
}

function genWxAppAccessTokenKey() {
  return `${WXWORK_AGENTID}:access-token`;
}

// 获取企业微信 Access Token
async function getToken() {
  const cacheKey = genWxAppAccessTokenKey();
  const cached = cache.get(cacheKey);
  if (cached && cached.expires >= Date.now()) return cached.token;

  const res = await axios.get('https://qyapi.weixin.qq.com/cgi-bin/gettoken', {
    params: {
      corpid: WXWORK_CORPID,
      corpsecret: WXWORK_CORPSECRET,
    },
  });

  const token = res.data.access_token;
  cache.set(cacheKey, {
    token,
    expires: Date.now() + res.data.expires_in * 1000,
  });
  return token;
}

// 发送企业微信消息
async function sendWxMessage(message, user) {
  const token = await getToken();
  response = await axios.post('https://qyapi.weixin.qq.com/cgi-bin/message/send', {
    touser: user,
    msgtype: 'text',
    agentid: WXWORK_AGENTID,
    text: { content: message },
    safe: 0,
    enable_id_trans: 0,
    enable_duplicate_check: 0,
    duplicate_check_interval: 1800,
  }, {
    params: { access_token: token },
  });
}

// 发送消息到 Dify
async function sendDifyMessage(message, userName, onMessage) {
  const conversationId = cache.get(genConversationKey(userName)) || null;
  let newConversationId = ''
  let responseText = '';

  try {
    const response = await axios.post(DIFY_BASE_URL,
      {
        inputs: {},
        response_mode: 'streaming',
        query: message,
        user: userName,
        conversation_id: conversationId,
      },
      {
        headers: { Authorization: `Bearer ${DIFY_APPTOKEN}` },
        responseType: 'stream',
      }
    );

    response.data.on('data', (chunk) => {
      const message = chunk.toString();
      if (message.startsWith('data: ')) {
        try {
	  // 解析数据
          const data = JSON.parse(message.substring(6));

	  // 多轮对话
          if (!newConversationId) {
              newConversationId = data.conversation_id
              cache.set(genConversationKey(userName), newConversationId)
          }
	  
	  // 接收返回数据
          if (data.event == "message") {
            responseText += data.answer;
          }
        } catch (e) {
          console.error('解析 Dify 响应失败:', e);
        }
      }
    });

    response.data.on('end', () => {
      if (responseText) onMessage(responseText.replace('\n\n', ''));
    });
  } catch (e) {
    console.error('Dify 请求失败:', e);
  }
}

// 处理企业微信消息
async function asyncSendMessage(xml) {
  if (xml.MsgType[0] !== 'text') return;

  const message = xml.Content[0];
  const userName = xml.FromUserName[0];

  if (message === '/new') {
    cache.del(genConversationKey(userName));
    await sendWxMessage('新建成功，开始新的对话吧~~', userName);
    return;
  }
  console.log('AI思考中, 请耐心等待~~', userName)
  await sendWxMessage('AI思考中, 请耐心等待~~', userName);

  try {
    await sendDifyMessage(message, userName, (msg) => {
      sendWxMessage(msg, userName);
    });
  } catch (e) {
    console.error('发送 Dify 消息失败:', e);
    await sendWxMessage('接口请求失败，请联系管理员', userName);
  }
}

// Express 中间件
app.use(bodyParser.text({ type: 'application/xml' }));

// 企业微信验证接口
app.get('/', (req, res) => {
  const { msg_signature, timestamp, nonce, echostr } = req.query;
  const signature = getSignature(WXWORK_TOKEN, timestamp, nonce, echostr);

  if (signature !== msg_signature) {
    return res.status(401).json({ message: '签名验证失败', code: 401 });
  }

  const { message } = decrypt(WXWORK_AESKEY, echostr);
  res.send(message);
});

// 接收企业微信消息
app.post('/', async (req, res) => {
  const { msg_signature, timestamp, nonce } = req.query;
  const xml = await xml2js.parseStringPromise(req.body);
  const encrypt = xml.xml.Encrypt[0];
  const signature = getSignature(WXWORK_TOKEN, timestamp, nonce, encrypt);
  if (signature !== msg_signature) {
    return res.status(401).json({ message: '签名验证失败', code: 401 });
  }
  const { message } = decrypt(WXWORK_AESKEY, encrypt);
  const { xml: parsedXml } = await xml2js.parseStringPromise(message);

  // 先返回 200 避免超时
  res.status(200).end();

  // 异步处理消息
  asyncSendMessage(parsedXml).catch(console.error);
});

// 启动服务器
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});
